#include "stdafx.h"
#include <atlbase.h>
#include <cemapi.h>
#include <mapiutil.h>
#include "CETool.h"

namespace CETool
{
	/////////////////////////////////////////////////////////////////////////////////////////////
	// This function is used to get the msgstore named SMS from msgstores on the
	// device.
	// I could have used just raw pointers but it is much easier and safer to just
	// use smart pointers.
	HRESULT GetSMSMsgStore(const CComPtr<IMAPISession>& spSession, CComPtr<IMsgStore>& spMsgStore)
	{
		// first we get the msgstores table from the session
		CComPtr<IMAPITable> spTable;
		HRESULT hr = spSession->GetMsgStoresTable(MAPI_UNICODE, &spTable);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_MSGSTORE_TABLES);
			return FALSE;
		}

		// next we loop over the message stores opening each msgstore and
		// getting its name to see if the name matches SMS.
		// If it does then we break out of the loop
		while (TRUE)
		{
			SRowSet* pRowSet = NULL;
			hr = spTable->QueryRows(1, 0, &pRowSet);

			// If we failed to query the
			// rows then we need to break
			if (FAILED(hr))
			{
				//AfxMessageBox(IDS_SMS_FAILEDTABLE);
				break;
			}

			// if we got no rows back then just exit the loop
			//remembering to set an error
			if (pRowSet->cRows == 1)
			{
				ASSERT(pRowSet->aRow[0].lpProps->ulPropTag == PR_ENTRYID);
				SBinary& blob = pRowSet->aRow[0].lpProps->Value.bin;
				hr = spSession->OpenMsgStore(NULL, blob.cb, (LPENTRYID)blob.lpb, NULL, 0, &spMsgStore);

				if (FAILED(hr))
				{
					//AfxMessageBox(IDS_SMS_FAILED_OPENMSGSTORE);
				}

			}
			else
			{
				//AfxMessageBox(IDS_SMS_MSGSTORENOTFOUND);
				hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
			}

			// now remember to free the row set
			FreeProws(pRowSet);

			if (FAILED(hr))
			{
				break;
			}

			// now get the display name property from the
			// message store to compare it against the name
			// 'SMS'
			SPropTagArray props;
			props.cValues = 1;
			props.aulPropTag[0] = PR_DISPLAY_NAME;

			ULONG cValues;
			SPropValue* pProps = NULL;
			hr = spMsgStore->GetProps(&props, MAPI_UNICODE, &cValues, &pProps);

			if (FAILED(hr) || cValues != 1)
			{
				//AfxMessageBox(IDS_SMS_FAILED_GETNAME);
				break;
			}

			// if the name matches SMS then break and as
			// hr == S_OK the current MsgStore smart pointer
			// will correctly be set.
			if (_tcsicmp(pProps[0].Value.lpszW, _T("SMS")) == 0)
			{
				break;
			}
			else
			{
				spMsgStore.Release();
			}
		}

		// if we failed for some reason then we clear out
		// the msgstore smartpointer and return the error.
		if (FAILED(hr))
		{
			spMsgStore.Release();
		}

		return hr;
	}


	/////////////////////////////////////////////////////////////////////////////////////////////
	// This function is used to get the folder named drafts from the msgstore on the
	// device.
	// I could have used just raw pointers but it is much easier and safer to just
	// use smart pointers.
	HRESULT GetSMSFolder(const CComPtr<IMsgStore>& spMsgStore, CComPtr<IMAPIFolder>& spFolder)
	{
		// Now get the Drafts folder.
		SPropTagArray propDefaultFolder;
		propDefaultFolder.cValues = 1;
		propDefaultFolder.aulPropTag[0] = PR_CE_IPM_DRAFTS_ENTRYID;

		ULONG			cValues;
		LPSPropValue	pPropVals;
		HRESULT hr = spMsgStore->GetProps (&propDefaultFolder, MAPI_UNICODE, &cValues, &pPropVals);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FOLDERNOTFOUND);
			return hr;
		}

		SBinary& eidDrafts = pPropVals->Value.bin;

		hr = spMsgStore->OpenEntry(eidDrafts.cb, (LPENTRYID)eidDrafts.lpb, NULL, MAPI_MODIFY, NULL, (LPUNKNOWN*) & spFolder);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FOLDERNOTOPENED);
		}

		return hr;
	}

	/////////////////////////////////////////////////////////////////////////////////////////////
	// This function is used to get the send the message.
	// This uses an opened MAPI session
	HRESULT SendSMSMessage(const CComPtr<IMAPISession>& spSession,
	                       LPCTSTR lpszFrom,
	                       LPCTSTR lpszTo,
	                       LPCTSTR lpszMessage)
	{
		// now get the SMS message store
		CComPtr<IMsgStore> spMsgStore;
		HRESULT hr = GetSMSMsgStore(spSession, spMsgStore);

		if (FAILED(hr))
		{
			return hr;
		}

		CComPtr<IMAPIFolder> spFolder;

		hr = GetSMSFolder(spMsgStore, spFolder);

		if (FAILED(hr))
		{
			return hr;
		}

		CComPtr<IMessage> spMessage;
		hr = spFolder->CreateMessage(NULL, 0 , &spMessage);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_CREATEMESSAGE);
			return hr;
		}

		// set the recipients
		// set up the required fields for a recipient
		SPropValue propRecipient[3];
		// it is vital we clear the property structure
		// as there are fields we do not use but MAPI seems
		// to be sentative to them.
		ZeroMemory(&propRecipient, sizeof(propRecipient));
		// set the recipient type which coul be to, cc, bcc
		// but ehre must at least be a to field
		propRecipient[0].ulPropTag = PR_RECIPIENT_TYPE;
		propRecipient[0].Value.l = MAPI_TO;

		// we set the type of address to sms instead of
		// smtp
		propRecipient[1].ulPropTag = PR_ADDRTYPE;
		propRecipient[1].Value.lpszW = _T("SMS");
		// we finally set the email address to the
		// phone number of the person we are sending the message
		// to
		propRecipient[2].ulPropTag = PR_EMAIL_ADDRESS;
		propRecipient[2].Value.lpszW = (LPWSTR)lpszTo;

		// set the addrlist to point to the properties
		ADRLIST adrlist;
		adrlist.cEntries = 1;
		adrlist.aEntries[0].cValues = 3;
		adrlist.aEntries[0].rgPropVals = (LPSPropValue)(&propRecipient);

		// finally modify the recipients of the message
		hr = spMessage->ModifyRecipients(MODRECIP_ADD, &adrlist);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAILED_ADDRECIPIENTS);
			return hr;
		}
		else
		{
			; // added the recipient to the message
		}

		// now we set the additional properties for the
		// message
		SPropValue props[4];

		//note how we zero out the contents of the
		// structure as MAPI is sensative to the
		// contents of other fields we do not use.
		ZeroMemory(&props, sizeof(props));

		// first set the subject of the message
		// as the sms we are going to send
		props[0].ulPropTag = PR_SUBJECT;
		props[0].Value.lpszW = (LPWSTR)lpszMessage;

		// next set the senders email address to
		// the phone number of the person we are
		// sending the message to
		props[1].ulPropTag = PR_SENDER_EMAIL_ADDRESS;
		props[1].Value.lpszW = (LPWSTR)lpszFrom;

		// finally and most importantly tell mapi
		// this is a sms message in need of delivery
		props[2].ulPropTag = PR_MSG_STATUS;
		props[2].Value.ul = MSGSTATUS_RECTYPE_SMS;

		props[3].ulPropTag = PR_MESSAGE_FLAGS;
		props[3].Value.ul = MSGFLAG_FROMME | MSGFLAG_UNSENT;

		hr = spMessage->SetProps(sizeof(props) / sizeof(props[0]), (LPSPropValue) & props, NULL);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_SETPROPS);
			return hr;
		}

		// having set all the required fields we can now
		// pass the message over to the msgstore transport
		// to be delivered.
		hr = spMessage->SubmitMessage(0);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_SUBMITMSG);
			return hr;
		}

		return FALSE;
	}

	/////////////////////////////////////////////////////////////////////////
	// This is the function that creates the session, using the
	// from, the recipient and the message.
	// This opens the session, opens the sms message store and opens
	// the drafts folder then create a new message and sets the sender,
	// recipient and messag, then finally sends the message.
	BOOL DoSendMessage(LPCTSTR lpszFrom, LPCTSTR lpszTo, LPCTSTR lpszMessage)
	{

		HRESULT hr = MAPIInitialize(NULL);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_MAPIINIT);
			return hr;
		}
		else
		{
			; // initialized the MAPI subsystem
		}

		CComPtr<IMAPISession> spSession;

		BOOL bRet = FALSE;

		hr = MAPILogonEx(0 , NULL, NULL, 0, &spSession);

		if (FAILED(hr))
		{
			//AfxMessageBox(IDS_SMS_FAIL_MAPILOGON);
		}
		else
		{
			bRet = SUCCEEDED(SendSMSMessage(spSession, lpszFrom, lpszTo, lpszMessage));

			spSession->Logoff(0, 0, 0);

			spSession.Release();
		}

		MAPIUninitialize();

		return bRet;
	}
}